import platform
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
from typing import List, Dict, Any, Optional, Callable
import os
import json
from datetime import datetime
import hashlib
from concurrent.futures import ThreadPoolExecutor
import threading
import subprocess  # 新增导入
from pathlib import Path


class CleanerConfig:
    """清理器配置类，管理系统类型、目标目录和平台特定配置"""
    
    # 通用软件包扩展名配置
    COMMON_PACKAGE_EXTS = {
        'windows': ['exe', 'msi', 'zip', 'rar', '7z', 'iso', 'cab', 'appx'],
        'linux': ['deb', 'rpm', 'tar.gz', 'tar.xz', 'zip', 'rar', 'sh'],
        'macos': ['dmg', 'pkg', 'zip', 'rar', 'xip']
    }
    
    def __init__(self, system_type: str = None, clean_dirs: List[str] = None, 
                 link_type: str = 'delete', full_scan: bool = False,
                 hash_algorithm: str = 'md5'):  # 新增参数
        self.hash_algorithm = hash_algorithm  # 新增属性
        self.system_type = system_type or self._detect_system()
        self.full_scan = full_scan  # 新增：全盘扫描标志
        self.target_dirs = clean_dirs or (self._get_full_disk_dirs() if full_scan else self._get_default_dirs())
        self.link_type = link_type
        self.check_folders = False
        self.folder_depth = 3
        self.platform_config = self._get_platform_config()
        self.custom_exts = []  # 用户自定义的软件包扩展名
        self.include_keywords = []       # 包含的关键字列表（支持正则表达式）
        self.keyword_logic = "or"        # 逻辑关系：'or'（或）/'and'（与）
        self.recursive = True            # 是否递归扫描子目录

    @staticmethod
    def get_hash_algorithms():
        return {
            'md5': {'name': 'MD5', 'speed': '快', 'security': '较低'},
            'sha1': {'name': 'SHA-1', 'speed': '中等', 'security': '中'},
            'sha256': {'name': 'SHA-256', 'speed': '较慢', 'security': '高'},
            'blake2': {'name': 'BLAKE2', 'speed': '最快', 'security': '高'}
        }

    def set_keyword_filters(self, keywords: List[str], logic: str = "or", recursive: bool = True):
        """设置目录过滤关键字"""
        self.include_keywords = [kw.strip() for kw in keywords if kw.strip()]
        self.keyword_logic = logic.lower()
        self.recursive = recursive

    def is_dir_allowed(self, dir_path: str) -> bool:
        """判断目录是否符合过滤条件"""
        if not self.include_keywords:
            return True  # 无过滤时允许所有目录
        
        dir_name = os.path.basename(dir_path)
        matches = [bool(re.search(kw, dir_name, re.IGNORECASE)) for kw in self.include_keywords]
        
        if self.keyword_logic == "or":
            return any(matches)
        elif self.keyword_logic == "and":
            return all(matches)
        return False
        
    def _detect_system(self) -> str:
        system = platform.system().lower()
        if system == 'windows':
            return 'windows'
        elif system == 'linux':
            return 'linux'
        elif system == 'darwin':
            return 'macos'
        return 'unknown'
    
    def _get_full_disk_dirs(self) -> List[str]:
        """获取用于全盘扫描的目录列表"""
        if self.system_type == 'windows':
            drives = []
            try:
                import win32api
                drives = win32api.GetLogicalDriveStrings().split('\000')[:-1]
            except:
                drives = ['C:\\']  # 至少扫描C盘
            return drives
        elif self.system_type == 'linux':
            return ['/']  # 根目录
        elif self.system_type == 'macos':
            return ['/']  # 根目录
        return [os.path.expanduser('~')]
    
    def _get_default_dirs(self) -> List[str]:
        """获取默认扫描目录（非全盘扫描时使用）"""
        if self.system_type == 'windows':
            return [
                os.path.expanduser('~\\Downloads'),
                os.path.expanduser('~\\Documents'),
                os.path.expanduser('~\\Desktop')
            ]
        elif self.system_type == 'linux':
            return [os.path.expanduser('~/Downloads'), os.path.expanduser('~/Documents')]
        elif self.system_type == 'macos':
            return [os.path.expanduser('~/Downloads'), os.path.expanduser('~/Documents')]
        return [os.path.expanduser('~')]
 
    def _get_platform_config(self) -> dict:
        config = {
            'windows': {
                'package_ext': self.COMMON_PACKAGE_EXTS['windows'],
                'temp_dirs': [
                    os.path.expanduser('~\\AppData\\Local\\Temp'),
                    os.path.join(os.environ.get('WINDIR', 'C:\\Windows'), 'Temp')
                ]
            },
            'linux': {
                'package_ext': self.COMMON_PACKAGE_EXTS['linux'],
                'temp_dirs': ['/tmp', os.path.expanduser('~/.cache')]
            },
            'macos': {
                'package_ext': self.COMMON_PACKAGE_EXTS['macos'],
                'temp_dirs': [
                    os.path.expanduser('~/Library/Caches'),
                    os.path.expanduser('~/Library/Application Support/Caches')
                ]
            }
        }
        return config.get(self.system_type, {})
    
    def add_custom_extensions(self, exts: List[str]):
        """添加用户自定义的软件包扩展名"""
        self.custom_exts.extend([ext.strip().lower() for ext in exts if ext.strip()])
        
    def get_all_package_extensions(self) -> List[str]:
        """获取所有软件包扩展名（内置+自定义）"""
        platform_exts = self.platform_config.get('package_ext', [])
        return list(set(platform_exts + self.custom_exts))

import re

class BaseCleaner:
    """清理器基类，定义扫描和清理的接口"""
    
    def __init__(self, config: CleanerConfig):
        self.config = config
        self.scan_results = []

    def _recursive_scan(self, root_dir: str, progress_callback=None) -> List[str]:
        """带目录过滤的递归扫描方法"""
        allowed_files = []
        print(f"开始扫描根目录: {root_dir}")  # 调试日志
        for current_dir, sub_dirs, files in os.walk(root_dir):
            # 检查当前目录是否允许扫描
            if not self.config.is_dir_allowed(current_dir):
                continue  # 跳过不符合条件的目录
            
             # 检查当前目录是否允许扫描
            allowed = self.config.is_dir_allowed(current_dir)
            print(f"检查目录: {current_dir} -> 允许: {allowed}")  # 调试日志

            # 处理文件
            for file in files:
                file_path = os.path.join(current_dir, file)
                allowed_files.append(file_path)
                # 更新进度
                if progress_callback:
                    self._update_progress(progress_callback)
            
            # 处理子目录（根据递归设置）
            if self.config.recursive:
                # 过滤子目录（仅保留符合条件的子目录进行递归）
                sub_dirs[:] = [d for d in sub_dirs if self.config.is_dir_allowed(os.path.join(current_dir, d))]
            else:
                sub_dirs.clear()  # 禁止递归时清空子目录列表
        
        return allowed_files
    
    def _update_progress(self, progress_callback):
        """简化的进度更新逻辑（需根据实际情况实现）"""
        if progress_callback:
            progress_callback(1)  # 示例：每次处理一个文件更新1%

        
    def scan(self, progress_callback: Optional[Callable[[int], None]] = None) -> List[str]:
        """执行扫描操作，返回可清理的项目列表"""
        raise NotImplementedError("子类必须实现scan方法")
        
    def clean(self) -> int:
        """执行清理操作，返回已清理的项目数量"""
        raise NotImplementedError("子类必须实现clean方法")
        
    def save_scan_results(self, scan_type: str) -> dict:
        """保存扫描结果到JSON文件"""
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        os.makedirs('scan_results', exist_ok=True)
        filename = f"scan_results/{scan_type}_{timestamp}.json"
        
        results = {
            'timestamp': timestamp,
            'scan_type': scan_type,
            'system': self.config.system_type,
            'target_dirs': self.config.target_dirs,
            'files': self.scan_results
        }
        
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(results, f, indent=2)
            
        return results

class SmartDuplicateCleaner(BaseCleaner):
    """智能重复文件清理器，支持基于哈希的文件比较和文件夹扫描"""
    
    def __init__(self, config: CleanerConfig):
        super().__init__(config)
        self.duplicate_groups = {}
        self.duplicate_folders = {}
        self.stop_event = threading.Event()  # 用于停止扫描的事件
        
    def scan(self, progress_callback: Optional[Callable[[int], None]] = None) -> List[str]:
        """执行重复文件和文件夹扫描"""
        print("🔍 开始扫描重复项...")
        self.stop_event.clear()
        
        # 计算总文件数用于进度计算
        total_files = self._count_total_files()
        processed_files = 0
        
        # 仅当未启用文件夹检测时才扫描文件
        if not self.config.check_folders:
            # 文件扫描
            size_map = self._group_files_by_size(progress_callback, total_files, processed_files)
            if self.stop_event.is_set():
                return []
                
            self.duplicate_groups = self._find_duplicate_hashes(size_map, progress_callback)
            if self.stop_event.is_set():
                return []
        else:
            self.duplicate_groups = {}  # 清空文件扫描结果
        
        # 总是执行文件夹扫描（当 check_folders 启用时）
        if self.config.check_folders:
            self._scan_duplicate_folders(progress_callback)
        
        self._save_scan_log()
        self.scan_results = self._generate_clean_list()
        
        # 确保进度条达到100%
        if progress_callback:
            progress_callback(100)
            
        return self.scan_results
    
    def stop(self):
        """停止正在进行的扫描"""
        self.stop_event.set()
    
    def _count_total_files(self) -> int:
        """计算所有目标目录中的文件总数"""
        count = 0
        for dir_path in self.config.target_dirs:
            try:
                for _, _, files in os.walk(dir_path):
                    count += len(files)
            except Exception as e:
                print(f"无法遍历目录 {dir_path}: {e}")
        return count
    
    def _group_files_by_size(self, progress_callback, total_files, processed_files) -> Dict[int, List[str]]:
        size_map = {}
        processed_files = 0
        
        for dir_path in self.config.target_dirs:
            allowed_files = self._recursive_scan(dir_path, progress_callback)
            for file_path in allowed_files:
                try:
                    size = os.path.getsize(file_path)
                    if size == 0:
                        continue
                    size_map.setdefault(size, []).append(file_path)
                except (PermissionError, FileNotFoundError) as e:
                    print(f"无法处理文件 {file_path}: {e}")
        
        return {size: files for size, files in size_map.items() if len(files) > 1}
    
    def _find_duplicate_hashes(self, size_map: dict, progress_callback=None) -> dict:
        """计算相同大小文件的哈希值，找出真正的重复文件"""
        hash_map = {}
        total_groups = len(size_map)
        processed_groups = 0
        
        # 使用线程池并行计算哈希值
        with ThreadPoolExecutor(max_workers=4) as executor:
            future_to_file = {}
            
            # 提交所有文件的哈希计算任务
            for size, files in size_map.items():
                if self.stop_event.is_set():
                    return {}
                    
                for file_path in files:
                    future = executor.submit(self._calculate_file_hash, file_path)
                    future_to_file[future] = file_path
                
                # 更新进度
                processed_groups += 1
                if progress_callback and total_groups > 0:
                    progress = processed_groups / total_groups * 100
                    progress_callback(progress)
            
            # 收集结果
            for future in future_to_file:
                if self.stop_event.is_set():
                    return {}
                    
                try:
                    file_hash = future.result()
                    file_path = future_to_file[future]
                    if file_hash in hash_map:
                        hash_map[file_hash].append(file_path)
                    else:
                        hash_map[file_hash] = [file_path]
                except Exception as e:
                    print(f"无法计算文件哈希: {e}")
        
        # 过滤掉没有重复哈希的文件
        return {file_hash: files for file_hash, files in hash_map.items() if len(files) > 1}
    
    def _calculate_file_hash(self, file_path: str, block_size=65536) -> str:
        if not os.path.exists(file_path):  # 检查文件是否存在
            return ""
        if self.stop_event.is_set():
            return ""

        hash_algo = self.config.hash_algorithm   
        try:
            # 根据配置选择哈希算法
            if hash_algo == 'md5':
                hasher = hashlib.md5()
            elif hash_algo == 'sha1':
                hasher = hashlib.sha1()
            elif hash_algo == 'sha256':
                hasher = hashlib.sha256()
            elif hash_algo == 'blake2':
                hasher = hashlib.blake2b()
            else:
                hasher = hashlib.md5()  # 默认使用MD5
            with open(file_path, 'rb') as f:
                buf = f.read(block_size)
                while buf:
                    if self.stop_event.is_set():
                        return ""
                    hasher.update(buf)
                    buf = f.read(block_size)
            return hasher.hexdigest()
        except (PermissionError, FileNotFoundError) as e:
            print(f"无法计算文件 {file_path} 的哈希值: {e}")
            return ""
    
    def _scan_duplicate_folders(self, progress_callback=None):
        """扫描重复文件夹（基于大小和内容）"""
        print("🔍 开始扫描重复文件夹...")
        folder_map = {}
        
        # 先计算所有文件夹的信息
        folder_infos = []
        for dir_path in self.config.target_dirs:
            try:
                for root, dirs, _ in os.walk(dir_path):
                    # 限制扫描深度
                    depth = root.count(os.sep) - dir_path.count(os.sep)
                    if depth > self.config.folder_depth:
                        continue
                    
                    for folder in dirs:
                        folder_path = os.path.join(root, folder)
                        try:
                            # 使用文件夹大小和文件列表作为相似度指标
                            folder_info = self._get_folder_info(folder_path)
                            if folder_info:
                                folder_infos.append(folder_info)
                        except Exception as e:
                            print(f"无法处理文件夹 {folder_path}: {e}")
            except Exception as e:
                print(f"无法遍历目录 {dir_path}: {e}")
        
        # 分组相似的文件夹
        total_folders = len(folder_infos)
        for i, info in enumerate(folder_infos):
            if self.stop_event.is_set():
                return
                
            key = (info['size'], tuple(sorted(info['files'])))
            if key in folder_map:
                folder_map[key].append(info['path'])
            else:
                folder_map[key] = [info['path']]
            
            # 更新进度
            if progress_callback and total_folders > 0:
                progress = (i + 1) / total_folders * 100
                progress_callback(progress)
        
        # 过滤掉没有重复的文件夹
        self.duplicate_folders = {key: folders for key, folders in folder_map.items() if len(folders) > 1}
    
    def _get_folder_info(self, folder_path: str) -> dict:
        """获取文件夹的信息（大小和文件列表）"""
        total_size = 0
        file_list = []
        
        try:
            for dirpath, _, filenames in os.walk(folder_path):
                for f in filenames:
                    fp = os.path.join(dirpath, f)
                    try:
                        size = os.path.getsize(fp)
                        total_size += size
                        file_list.append(f)
                    except Exception:
                        pass
        except Exception:
            return None
            
        return {
            'path': folder_path,
            'size': total_size,
            'files': file_list
        }
    
    def _save_scan_log(self):
        """保存扫描日志"""
        pass  # 简化实现，实际应保存扫描日志
    
    def _generate_clean_list(self) -> List[str]:
        """生成可清理的项目列表（保留最新的文件/文件夹）"""
        clean_list = []
        
        # 添加重复文件（保留最新的一个）
        for files in self.duplicate_groups.values():
            # 按修改时间排序，最新的文件排在前面
            try:
                files.sort(key=lambda x: os.path.getmtime(x), reverse=True)
                # 保留第一个（最新的），其余添加到清理列表
                clean_list.extend(files[1:])
            except Exception as e:
                print(f"排序文件时出错: {e}")
        
        # 添加重复文件夹（保留最新的一个）
        if hasattr(self, 'duplicate_folders'):
            for files in self.duplicate_folders.values():
                try:
                    files.sort(key=lambda x: os.path.getmtime(x), reverse=True)
                    clean_list.extend(files[1:])
                except Exception as e:
                    print(f"排序文件夹时出错: {e}")
        
        return clean_list
    
    def save_scan_results(self, scan_type: str) -> dict:
        """保存详细的扫描结果"""
        from pathlib import Path
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        os.makedirs('scan_results', exist_ok=True)
        filename = f"scan_results/{scan_type}_{timestamp}.json"
        
        # 保存分组数据（每个分组包含文件列表）
        groups = []
        for file_hash, files in self.duplicate_groups.items():
            # 规范化所有路径
            normalized_files = []
            for f in files:
                normalized_path = str(Path(f).resolve())
                normalized_files.append(normalized_path)
            
            try:
                groups.append({
                    "hash": file_hash,
                    "files": normalized_files,  # 保存规范化后的路径
                    "count": len(files),
                    "total_size": sum(os.path.getsize(f) for f in files if os.path.exists(f))
                })
            except Exception as e:
                print(f"保存扫描结果时出错: {e}")
        
        # 保存文件夹数据
        folder_groups = []
        if hasattr(self, 'duplicate_folders'):
            for (size, file_tuple), folders in self.duplicate_folders.items():
                try:
                    # 规范化文件夹路径
                    normalized_folders = []
                    for folder in folders:
                        normalized_path = str(Path(folder).resolve())
                        normalized_folders.append(normalized_path)
                    
                    folder_groups.append({
                        "size": size,
                        "files": list(file_tuple),
                        "folders": normalized_folders,
                        "count": len(folders),
                        "total_size": size
                    })
                except Exception as e:
                    print(f"保存文件夹扫描结果时出错: {e}")
        
        results = { # 使用绝对路径保存
            'timestamp': timestamp,
            'scan_type': scan_type,
            'system': self.config.system_type,
            'target_dirs': self.config.target_dirs,
            'groups': groups,  # 保存文件分组信息
            'folder_groups': folder_groups,  # 保存文件夹分组信息
            'clean_list': self.scan_results  # 保存清理列表
        }
        
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(results, f, indent=2, ensure_ascii=False)  # 允许保存非ASCII字符
            
        return results

class PackageCleaner(BaseCleaner):
    """软件包清理器，扫描并清理安装包和临时文件"""
    
    def __init__(self, config: CleanerConfig):
        super().__init__(config)
        self.stop_event = threading.Event()  # 用于停止扫描的事件


    def save_scan_results(self, scan_type: str) -> dict:
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        os.makedirs('scan_results', exist_ok=True)
        filename = f"scan_results/{scan_type}_{timestamp}.json"
        
        results = {
            'timestamp': timestamp,
            'scan_type': scan_type,
            'system': self.config.system_type,
            'target_dirs': self.config.target_dirs,
            'files': self.scan_results  # 直接保存字典列表
        }
        
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(results, f, indent=2)
            
        return results
        
    def scan(self, progress_callback=None) -> List[dict]:
        package_ext = self.config.get_all_package_extensions()

        results = []
        
        allowed_files = []
        for dir_path in self.config.target_dirs:
            allowed_files.extend(self._recursive_scan(dir_path, progress_callback))

        for file_path in allowed_files:
            if '.' in file_path:
                file_ext = file_path.split('.')[-1].lower()
                if file_ext in package_ext:
                    try:
                        if os.path.exists(file_path):
                            
                                size = os.path.getsize(file_path)
                                mtime = os.path.getmtime(file_path)
                                results.append({
                                    'path': file_path,
                                    'size': size,
                                    'mtime': self.validate_timestamp(mtime)
                                })                                                                 
                                # 添加调试日志（关键）
                                print(f"扫描到软件包: {file_path} (扩展名: {file_ext})")
                    except Exception as e:
                                print(f"获取文件信息失败: {file_path}, 错误: {e}")
                        
                else:
                    continue
        
        self.scan_results = results
        return results
    
    def validate_timestamp(self, ts: float) -> float:
        """验证时间戳有效性"""
        min_ts = 0  # 1970-01-01
        max_ts = datetime.now().timestamp() + 86400*365*10  # 当前时间+10年
        
        try:
            ts = float(ts)
            if min_ts <= ts <= max_ts:
                return ts
            else:
                print(f"无效时间戳 {ts}，已重置为当前时间")
                return datetime.now().timestamp()
        except (TypeError, ValueError):
            print(f"非数值时间戳 {ts}，已重置为当前时间")
            return datetime.now().timestamp()
    
    def stop(self):
        """停止正在进行的扫描"""
        self.stop_event.set()
    
    def _count_total_files(self) -> int:
        """计算所有目标目录中的文件总数"""
        count = 0
        for dir_path in self.config.target_dirs:
            try:
                for _, _, files in os.walk(dir_path):
                    count += len(files)
            except Exception as e:
                print(f"无法遍历目录 {dir_path}: {e}")
        return count

class CleanManager:
    """清理管理器，协调不同类型的清理器"""
    
    def __init__(self, system_type: str = None, clean_dirs: List[str] = None, 
                 link_type: str = 'delete', full_scan: bool = False,
                 hash_algorithm: str = 'md5'):  # 新增参数
        self.config = CleanerConfig(system_type, clean_dirs, 
                                    link_type, 
                                    full_scan,
                                    hash_algorithm=hash_algorithm  # 传递到配置
                                    )  # 传递全盘扫描标志
        self.cleaners = {
            'duplicate': SmartDuplicateCleaner(self.config),
            'package': PackageCleaner(self.config)
        }
        self.scan_data = {}
        self.stop_event = threading.Event()  # 用于停止所有扫描
    
    def run_scan(self, clean_types: List[str], progress_callback=None):
        """执行扫描，支持进度回调和中途停止"""
        if not self.config.target_dirs:
            print("⚠️ 警告：即将进行全盘扫描！")
            print("可能需要管理员权限且耗时较长")
            input("按 Enter 继续，Ctrl+C 取消...")
            
        self.stop_event.clear()
        total_steps = len(clean_types)
        
        for i, clean_type in enumerate(clean_types):
            if self.stop_event.is_set():
                return
                
            if clean_type not in self.cleaners:
                continue
                
            cleaner = self.cleaners[clean_type]
            
            # 为每个清理器创建进度回调
            def sub_progress_callback(progress):
                if progress_callback:
                    base_progress = i / total_steps * 100
                    step_progress = progress / total_steps
                    progress_callback(base_progress + step_progress)
            
            try:
                # 传递进度回调和停止事件给具体的扫描器
                files = cleaner.scan(progress_callback=sub_progress_callback)
                if self.stop_event.is_set():
                    return
                    
                result = cleaner.save_scan_results(clean_type)
                saved_path = f"scan_results/{clean_type}_{result['timestamp']}.json"
                self.scan_data[clean_type] = saved_path
                
                if clean_type == 'package':
                    # 软件包扫描结果是字典列表，提取'path'键
                    total_size = sum(f['size'] for f in files if os.path.exists(f['path'])) if files else 0
                else:
                    # 重复文件扫描结果是路径列表
                    total_size = sum(os.path.getsize(f) for f in files if os.path.exists(f)) if files else 0
                print(f"\n✅ {clean_type} 扫描完成")
                print(f"发现 {len(files)} 个可清理项")
                print(f"预计可释放空间: {total_size/1024/1024:.2f} MB")
                print(f"扫描结果已保存至: {saved_path}\n")
                
            except Exception as e:
                print(f"💥 {clean_type} 扫描器错误: {str(e)}")
            
            # 更新总体进度
            if progress_callback and not self.stop_event.is_set():
                progress = (i + 1) / total_steps * 100
                progress_callback(progress)
    
    def run_clean(self, clean_types: List[str]) -> int:
        """执行清理操作"""
        cleaned_count = 0
        
        # 确认对话框
        if not messagebox.askyesno("确认清理", "确定要删除选中的文件吗？此操作不可恢复！"):
            return 0
            
        for clean_type in clean_types:
            if clean_type not in self.cleaners:
                continue
                
            cleaner = self.cleaners[clean_type]
            try:
                # 针对软件包清理器，处理包含文件信息的字典列表
               if clean_type == 'package' and isinstance(cleaner.scan_results, list):
                   for item in cleaner.scan_results:
                       file_path = item.get('path', '')
                       if isinstance(file_path, str) and os.path.exists(file_path):
                           try:
                               if os.path.isfile(file_path):
                                   os.remove(file_path)
                                   cleaned_count += 1
                               elif os.path.isdir(file_path):
                                   import shutil
                                   shutil.rmtree(file_path)
                                   cleaned_count += 1
                           except Exception as e:
                               print(f"无法删除 {file_path}: {e}")
               else:
                   count = cleaner.clean()
                   cleaned_count += count
               print(f"✅ {clean_type} 清理完成，已清理 {cleaned_count} 个项目")  # 统一使用cleaned_count
            except Exception as e:
                print(f"💥 {clean_type} 清理器错误: {str(e)}")
                
        return cleaned_count
    
    def stop_scan(self):
        """停止所有正在进行的扫描"""
        self.stop_event.set()
        for cleaner in self.cleaners.values():
            cleaner.stop()

class EnhancedScanResultsWindow(tk.Toplevel):
    """增强版扫描结果窗口，支持更详细的展示和选择操作"""
    
    def __init__(self, parent, scan_type, results, parent_app):
        super().__init__(parent)
        self.parent_app = parent_app
        self.title(f"{scan_type} 扫描结果详情")
        self.geometry("900x600")
        self.resizable(True, True)
        self.scan_type = scan_type
        self.results = results
        self.link_type = "delete"  # 默认处理方式为删除
        self.status_var = tk.StringVar()
        self.tree = None  # 初始化tree属性
        
        # **新增：预定义排序变量**
        self.sort_var = tk.StringVar(value="size")
        self.sort_reverse_var = tk.BooleanVar(value=True)

        # 创建UI
        self.create_widgets()
        
        # 窗口关闭时释放资源
        self.bind_right_click()
        self.protocol("WM_DELETE_WINDOW", self.on_close)

    def create_sort_controls(self, parent_frame):
        """创建排序控制组件"""
        sort_frame = ttk.Frame(parent_frame)
        sort_frame.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=10)
        
        ttk.Label(sort_frame, text="排序方式:").pack(side=tk.LEFT, padx=5)
        
        # 排序类型选择
        self.sort_type = tk.StringVar(value="size")
        ttk.Radiobutton(sort_frame, text="按大小", variable=self.sort_type, value="size").pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(sort_frame, text="按修改时间", variable=self.sort_type, value="mtime").pack(side=tk.LEFT, padx=5)
        
        # 排序顺序选择
        self.sort_order = tk.StringVar(value="desc")
        ttk.Radiobutton(sort_frame, text="降序", variable=self.sort_order, value="desc").pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(sort_frame, text="升序", variable=self.sort_order, value="asc").pack(side=tk.LEFT, padx=5)
        
        # 应用排序按钮
        ttk.Button(sort_frame, text="应用排序", command=self.apply_sort).pack(side=tk.LEFT, padx=5)
        
    def create_widgets(self):
        # 顶部控制栏
        control_frame = ttk.Frame(self)
        control_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Label(control_frame, text="处理方式:").pack(side=tk.LEFT, padx=5)
        self.link_type_var = tk.StringVar(value="delete")  # 初始化默认值
        
        # 根据扫描类型动态设置处理方式选项
        if self.scan_type != "package":
            link_options = [("直接删除", "delete"), ("符号链接", "symlink"), ("硬链接", "hardlink")]
        else:
            link_options = [("直接删除", "delete")]  # 软件包仅显示删除
        
        for text, value in link_options:
            ttk.Radiobutton(
                control_frame, 
                text=text, 
                variable=self.link_type_var, 
                value=value
            ).pack(side=tk.LEFT, padx=5)
        
        ttk.Button(control_frame, text="全选", command=self.select_all).pack(side=tk.LEFT, padx=5)
        ttk.Button(control_frame, text="反选", command=self.invert_selection).pack(side=tk.LEFT, padx=5)
        ttk.Button(control_frame, text="应用处理", command=self.apply_action).pack(side=tk.RIGHT, padx=5)
        
        # **新增：仅在非软件包扫描时添加排序控件**
        if self.scan_type != "package":
            ttk.Label(control_frame, text="排序方式:").pack(side=tk.LEFT, padx=5)
            sort_options = [("大小", "size"), ("修改时间", "mtime"), ("路径", "path")]
            self.sort_var = tk.StringVar(value="size")
            for text, value in sort_options:
                ttk.Radiobutton(
                    control_frame, 
                    text=text, 
                    variable=self.sort_var, 
                    value=value,
                    command=self.sort_results
                ).pack(side=tk.LEFT, padx=5)
            
            self.sort_reverse_var = tk.BooleanVar(value=True)
            ttk.Checkbutton(
                control_frame, 
                text="降序", 
                variable=self.sort_reverse_var,
                command=self.sort_results
            ).pack(side=tk.LEFT, padx=5)
        
        # 结果展示区域
        self.create_results_display()
        
        # 底部状态栏
        status_frame = ttk.Frame(self)
        status_frame.pack(fill=tk.X, side=tk.BOTTOM, padx=10, pady=5)
        
        self.status_var = tk.StringVar()
        ttk.Label(status_frame, textvariable=self.status_var).pack(side=tk.LEFT)
        self.update_status()

    def bind_right_click(self):
        """绑定右键菜单事件"""
        self.tree.bind("<Button-3>", lambda e: self.show_context_menu(e))  # 明确绑定
        self.tree.bind("<Control-Button-1>", self.on_tree_click)  # Mac右键模拟

    def show_context_menu(self, event):
        """显示右键上下文菜单"""
        item = self.tree.identify_row(event.y)
        if not item:
            return

        # 获取文件路径
        file_path = os.path.normpath(self.tree.item(item, "values")[2])  # 规范化路径
        menu = tk.Menu(self, tearoff=0)

        # 通用操作
        menu.add_command(label="打开文件位置", command=lambda: self.open_file_location(file_path))
        menu.add_command(label="查看文件属性", command=lambda: self.show_file_properties(file_path))
        
        # 检查文件是否存在
        if not os.path.exists(file_path):
            menu.add_command(label="文件不存在", state=tk.DISABLED)
            menu.post(event.x_root, event.y_root)
            return

        menu.add_command(label="删除文件", command=lambda: self.delete_single_file(file_path))

        # 仅限重复文件扫描的额外操作
        if self.scan_type == "duplicate":
            menu.add_separator()
            menu.add_command(label="创建符号链接", 
                           command=lambda: self.create_link(file_path, "symlink"))
            menu.add_command(label="创建硬链接",
                           command=lambda: self.create_link(file_path, "hardlink"))

        menu.post(event.x_root, event.y_root)

    def open_file_location(self, file_path):
        """打开文件所在目录"""
        if not os.path.exists(file_path):
            messagebox.showerror("错误", "文件不存在")
            return
        
        try:
            if os.name == 'nt':  # Windows
                cmd = f'explorer /select,"{os.path.normpath(file_path)}"'
                subprocess.Popen(cmd, shell=True)
            else:  # Linux/Mac
                dir_path = os.path.dirname(file_path)
                if platform.system() == 'Darwin':
                    script = f'''
                    tell application "Finder"
                        reveal POSIX file "{file_path}"
                        activate
                    end tell
                    '''
                    subprocess.Popen(['osascript', '-e', script])
                else:
                    subprocess.Popen(['xdg-open', '--select', file_path])
            
            self.parent_app.update_log(f"已打开文件位置: {file_path}")
        except Exception as e:
            messagebox.showerror("错误", f"无法打开目录: {str(e)}")
            self.parent_app.update_log(f"打开文件位置失败: {str(e)}")

    def show_file_properties(self, file_path):
        """显示文件属性窗口"""
        prop_win = tk.Toplevel(self)
        prop_win.title("文件属性")
        
        try:
            stat = os.stat(file_path)
            info = [
                f"路径: {file_path}",
                f"大小: {self.format_size(stat.st_size)}",
                f"创建时间: {datetime.fromtimestamp(stat.st_ctime).strftime('%Y-%m-%d %H:%M:%S')}",
                f"修改时间: {datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S')}",
                f"访问时间: {datetime.fromtimestamp(stat.st_atime).strftime('%Y-%m-%d %H:%M:%S')}"
            ]
            
            for text in info:
                ttk.Label(prop_win, text=text).pack(anchor=tk.W, padx=10, pady=5)
        except Exception as e:
            ttk.Label(prop_win, text=f"无法获取属性: {str(e)}").pack()

    def delete_single_file(self, file_path):
        """删除单个文件"""
        if not messagebox.askyesno("确认删除", f"确定要删除以下文件吗？\n{file_path}"):
            return
        
        try:
            if os.path.isfile(file_path):
                os.remove(file_path)
                self.parent_app.update_log(f"成功删除: {file_path}")
            elif os.path.isdir(file_path):
                import shutil
                shutil.rmtree(file_path)
                self.parent_app.update_log(f"成功删除文件夹: {file_path}")
            self.refresh_after_delete(file_path)
        except Exception as e:
            self.parent_app.update_log(f"删除失败: {str(e)}")
            messagebox.showerror("删除失败", f"无法删除文件: {str(e)}")

    def create_link(self, source_path, link_type):   # 修改日志调用方式
        """创建链接文件"""
        target_dir = filedialog.askdirectory(title="选择链接保存目录")
        if not target_dir:
            return
        
        try:
            file_name = os.path.basename(source_path)
            link_path = os.path.join(target_dir, f"{file_name}_{link_type}")
            
            if link_type == "symlink":
                if os.name == 'nt' and not ctypes.windll.shell32.IsUserAnAdmin():
                    raise PermissionError("需要管理员权限创建符号链接")
                os.symlink(source_path, link_path)
            elif link_type == "hardlink":
                os.link(source_path, link_path)
            
            messagebox.showinfo("成功", f"已创建{link_type}到: {link_path}")
            self.parent_app.update_log(f"已创建{link_type}到: {link_path}")  # 通过父应用记录日志
        except Exception as e:
            messagebox.showerror("创建失败", f"无法创建{link_type}: {str(e)}")
            self.parent_app.update_log(f"创建{link_type}失败: {str(e)}")  # 错误日志

    def refresh_after_delete(self, deleted_path):
        """删除后刷新列表"""
        for item in self.tree.get_children():
            if self.tree.item(item, "values")[2] == deleted_path:
                self.tree.delete(item)
                break
        self.update_status()
        self.parent_app.update_log(f"已刷新列表，删除: {deleted_path}")  # 新增日志


    def apply_action(self):
        """应用选定的处理方式到选中的文件"""
        selected_files = self.get_selected_files()
        if not selected_files:
            messagebox.showinfo("提示", "请先选择要处理的文件")
            return
            
        action_type = self.link_type_var.get()
        if action_type == "delete":
            if not messagebox.askyesno("确认删除", "确定要删除选中的文件吗？此操作不可恢复！"):
                return
        else:
            if not messagebox.askyesno("确认操作", f"确定要对选中的文件创建{action_type}吗？"):
                return
                
        success_count = 0
        failed_count = 0
        failed_files = []
        
        for file_path in selected_files:
            try:
                if action_type == "delete":
                    if os.path.isfile(file_path):
                        os.remove(file_path)
                    elif os.path.isdir(file_path):
                        import shutil
                        shutil.rmtree(file_path)
                    success_count += 1
                elif action_type == "symlink":
                    # 符号链接逻辑（需要目标文件存在）
                    target_dir = os.path.dirname(file_path)
                    link_name = os.path.basename(file_path) + ".link"
                    link_path = os.path.join(target_dir, link_name)
                    
                    # 检查是否有足够权限创建符号链接
                    if os.name == 'nt':  # Windows系统
                        # Windows需要管理员权限或启用开发人员模式
                        import ctypes
                        try:
                            if not ctypes.windll.shell32.IsUserAnAdmin():
                                messagebox.showwarning("权限不足", "在Windows上创建符号链接需要管理员权限")
                                failed_count += 1
                                failed_files.append(file_path)
                                continue
                        except:
                            pass
                    
                    os.symlink(file_path, link_path)
                    success_count += 1
                elif action_type == "hardlink":
                    # 硬链接逻辑
                    target_dir = os.path.dirname(file_path)
                    link_name = os.path.basename(file_path) + ".hardlink"
                    link_path = os.path.join(target_dir, link_name)
                    
                    os.link(file_path, link_path)
                    success_count += 1
                    
            except Exception as e:
                failed_count += 1
                failed_files.append(f"{file_path}: {str(e)}")
                
        # 显示处理结果
        result_msg = f"操作完成！成功: {success_count}, 失败: {failed_count}"
        if failed_count > 0:
            result_msg += "\n\n失败文件列表:\n" + "\n".join(failed_files)
            
        messagebox.showinfo("处理结果", result_msg)
        self.destroy()  # 关闭窗口

    def get_selected_files(self):
        """获取所有选中的文件"""
        selected_files = []
        for item_id in self.tree.get_children():
            if self.tree.item(item_id, "values")[0] == "✓":
                file_path = self.tree.item(item_id, "values")[2]
                selected_files.append(file_path)
        return selected_files
  
    
    def create_results_display(self):
        """根据扫描类型创建相应的结果展示"""
        if self.scan_type == "duplicate":
            self.create_duplicate_results_display()
        elif self.scan_type == "package":
            self.create_package_results_display()
            self.sort_results()
    
    def create_duplicate_results_display(self):
        """创建重复文件结果展示"""
        ttk.Label(self, text="重复文件扫描结果", font=("Arial", 14, "bold")).pack(pady=10)
        
        groups = self.results.get('groups', [])
        if not groups:
            ttk.Label(self, text="未发现重复文件").pack(pady=20)
            return
            
        total_duplicates = sum(len(group['files']) - 1 for group in groups)
        total_size = sum(group.get('total_size', 0) for group in groups)
        
        ttk.Label(self, text=f"共发现 {len(groups)} 组重复文件，可清理 {total_duplicates} 个文件，释放 {total_size/1024/1024:.2f} MB").pack(pady=5)
        
        # 创建一个Treeview来显示结果
        columns = ("select", "size", "path")
        self.tree = ttk.Treeview(self, columns=columns, show="headings")
        self.tree.heading("select", text="选择")
        self.tree.heading("size", text="大小")
        self.tree.heading("path", text="文件路径")
        self.tree.column("select", width=50, anchor=tk.CENTER)
        self.tree.column("size", width=100)
        self.tree.column("path", width=700)
        self.tree.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(self.tree, orient=tk.VERTICAL, command=self.tree.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.tree.configure(yscrollcommand=scrollbar.set)
        
        # 填充数据
        for i, group in enumerate(groups):
            files = group['files']
            files.sort(key=lambda x: os.path.getmtime(x), reverse=True)  # 最新的文件排在前面
            
            # 保留的文件
            main_file = files[0]
            size = os.path.getsize(main_file) if os.path.exists(main_file) else 0
            item = self.tree.insert("", tk.END, values=("", self.format_size(size), main_file))
            self.tree.item(item, tags=("main",))
            
            # 可清理的文件
            for file in files[1:]:
                size = os.path.getsize(file) if os.path.exists(file) else 0
                sub_item = self.tree.insert("", tk.END, values=("✓", self.format_size(size), file))
                self.tree.item(sub_item, tags=("duplicate",))
        
        # 设置样式
        style = ttk.Style()
        style.configure("Treeview", rowheight=25)
        style.configure("Treeview.Heading", font=("Arial", 10, "bold"))
        
        # 配置标签样式
        self.tree.tag_configure("main", foreground="green")
        self.tree.tag_configure("duplicate", foreground="red")
        
        # 绑定选择事件
        self.tree.bind("<Button-1>", self.on_tree_click)    

    def create_package_results_display(self):
        ttk.Label(self, text="软件包扫描结果", font=("Arial", 14, "bold")).pack(pady=10)
        
        files = self.results.get('files', []) 
        valid_files = [f for f in files if isinstance(f.get('path'), str) and os.path.exists(f['path'])]
        
        if not valid_files:
            ttk.Label(self, text="未发现可清理的软件包").pack(pady=20)
            columns = ("select", "size", "path", "mtime")
            self.tree = ttk.Treeview(self, columns=columns, show="headings")
            self.tree.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
            return
        
        total_size = sum(f.get('size', 0) for f in valid_files)
        
        ttk.Label(self, text=f"共发现 {len(valid_files)} 个软件包，可释放 {total_size/1024/1024:.2f} MB").pack(pady=5) 
        
        columns = ("select", "size", "path", "mtime")
        self.tree = ttk.Treeview(self, columns=columns, show="headings")
        self.tree.heading("select", text="选择")
        self.tree.heading("size", text="大小")
        self.tree.heading("path", text="文件路径")
        self.tree.heading("mtime", text="修改时间")
        self.tree.column("select", width=50, anchor=tk.CENTER)
        self.tree.column("size", width=100)
        self.tree.column("path", width=700, stretch=tk.YES) 
        self.tree.column("mtime", width=150)
        self.tree.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(self.tree, orient=tk.VERTICAL, command=self.tree.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.tree.configure(yscrollcommand=scrollbar.set)

        # 存储原始数据供排序使用
        self.original_data = valid_files
        
        # 默认按大小降序排序（初始显示）
        sorted_files = sorted(valid_files, key=lambda x: x['size'], reverse=True)
        
        for file_info in sorted_files:
            file_path = os.path.normpath(file_info['path'])  # 规范化路径
            size = file_info['size']
            try:
                print(file_info['mtime'])
                mtime = datetime.fromtimestamp(file_info['mtime']).strftime("%Y-%m-%d %H:%M:%S")
            except Exception as e:
                print(f"timestamp {file_path}: {e}")
            self.tree.insert("", tk.END, values=("✓", self.format_size(size), file_path, mtime))
        
        # 设置样式
        style = ttk.Style()
        style.configure("Treeview", rowheight=25)
        style.configure("Treeview.Heading", font=("Arial", 10, "bold"))
        
        # 绑定选择事件
        self.tree.bind("<Button-1>", self.on_tree_click)       

    def apply_sort(self):
        """应用排序"""
        files = self.results.get('files', [])
        valid_files = [f for f in files if os.path.exists(f.get('path', ''))]
        
        if not valid_files:
            return
        
        # 根据用户选择排序
        sort_key = self.sort_type.get()
        sort_reverse = self.sort_order.get() == "desc"
        
        sorted_files = sorted(valid_files, key=lambda x: x[sort_key], reverse=sort_reverse)
        
        # 清空并重新填充Treeview
        for item in self.tree.get_children():
            self.tree.delete(item)
            
        for file_info in sorted_files:
            size = file_info['size']
            mtime = datetime.fromtimestamp(file_info['mtime']).strftime("%Y-%m-%d %H:%M:%S")
            self.tree.insert("", tk.END, values=("✓", self.format_size(size), file_info['path'], mtime))
    
    def sort_results(self):
        """根据选择的排序方式对结果进行排序"""
        if not hasattr(self, 'original_data'):
            return
            
        sort_key = self.sort_var.get()
        reverse = self.sort_reverse_var.get()
        
        # 清空当前显示
        for item in self.tree.get_children():
            self.tree.delete(item)
        
        # 根据选择的键排序
        if sort_key == "size":
            sorted_files = sorted(self.original_data, key=lambda x: x['size'], reverse=reverse)
        elif sort_key == "mtime":
            sorted_files = sorted(
                    self.original_data,
                    key=lambda x: x.get('mtime', 0),
                    reverse=reverse
                )
        elif sort_key == "path":
            sorted_files = sorted(
                    self.original_data,
                    key=lambda x: x.get('path', '').lower(),
                    reverse=reverse
                )
        else:
            sorted_files = self.original_data
        
        # 重新插入排序后的项
        for file_info in sorted_files:
            file_path = os.path.normpath(file_info['path'])  # 规范化路径
            size = file_info['size']
                        
            # 安全转换时间戳
            try:
                mtime_ts = float(file_info.get('mtime', 0))
                mtime = datetime.fromtimestamp(mtime_ts).strftime("%Y-%m-%d %H:%M:%S")
            except (ValueError, TypeError, OSError) as e:
                mtime = "时间无效"
                print(f"时间戳转换错误: {file_info.get('mtime')}，错误: {str(e)}")
            
            self.tree.insert("", tk.END, values=("✓", self.format_size(size), file_path, mtime))
        
        self.update_status()
    
    def on_tree_click(self, event):
        """处理Treeview中的点击事件，特别是选择框的切换"""
        region = self.tree.identify_region(event.x, event.y)
        if region == "cell":
            column = int(self.tree.identify_column(event.x).replace('#', ''))
            if column == 1:  # 选择列
                item_id = self.tree.identify_row(event.y)
                current_value = self.tree.item(item_id, "values")[0]
                new_value = "✓" if current_value != "✓" else ""
                self.tree.set(item_id, column=0, value=new_value)
                self.update_status()
    
    def select_all(self):
        """全选所有可清理的项目"""
        for item_id in self.tree.get_children():
            tags = self.tree.item(item_id, "tags")
            if "main" not in tags:  # 不选择保留的文件
                self.tree.set(item_id, column=0, value="✓")
        self.update_status()
    
    def invert_selection(self):
        """反选所有项目"""
        for item_id in self.tree.get_children():
            tags = self.tree.item(item_id, "tags")
            if "main" not in tags:  # 不操作保留的文件
                current_value = self.tree.item(item_id, "values")[0]
                new_value = "✓" if current_value != "✓" else ""
                self.tree.set(item_id, column=0, value=new_value)
        self.update_status()
    
    def delete_selected(self):
        """删除选中的文件"""
        if not messagebox.askyesno("确认删除", "确定要删除选中的文件吗？此操作不可恢复！"):
            return
            
        selected_files = []
        for item_id in self.tree.get_children():
            if self.tree.item(item_id, "values")[0] == "✓":
                file_path = self.tree.item(item_id, "values")[2]
                selected_files.append(file_path)
        
        deleted_count = 0
        for file_path in selected_files:
            try:
                if os.path.isfile(file_path):
                    os.remove(file_path)
                    deleted_count += 1
                elif os.path.isdir(file_path):
                    import shutil
                    shutil.rmtree(file_path)
                    deleted_count += 1
            except Exception as e:
                print(f"无法删除 {file_path}: {e}")
        
        messagebox.showinfo("删除完成", f"已成功删除 {deleted_count} 个项目")
        self.destroy()
    
    def update_status(self):
        """更新状态栏信息"""
        selected_count = 0
        total_size = 0
        
        for item_id in self.tree.get_children():
            if self.tree.item(item_id, "values")[0] == "✓":
                selected_count += 1
                size_str = self.tree.item(item_id, "values")[1]
                try:
                    # 解析大小字符串，转换为字节
                    size_parts = size_str.split()
                    if len(size_parts) == 2:
                        size_num = float(size_parts[0])
                        size_unit = size_parts[1]
                        if size_unit == "KB":
                            size_num *= 1024
                        elif size_unit == "MB":
                            size_num *= 1024 * 1024
                        elif size_unit == "GB":
                            size_num *= 1024 * 1024 * 1024
                        total_size += size_num
                except:
                    pass
        
        # 使用当前窗口的status_var更新状态
        self.status_var.set(f"已选择 {selected_count} 个项目，释放空间: {total_size / 1024 / 1024:.2f} MB")
    
    def format_size(self, size_bytes):
        """将字节转换为更友好的格式"""
        if size_bytes < 1024:
            return f"{size_bytes} B"
        elif size_bytes < 1024 * 1024:
            return f"{size_bytes/1024:.2f} KB"
        elif size_bytes < 1024 * 1024 * 1024:
            return f"{size_bytes/1024/1024:.2f} MB"
        else:
            return f"{size_bytes/1024/1024/1024:.2f} GB"
    
    def on_close(self):
        self.destroy()

class EnhancedCleanUI(tk.Tk):
    """增强版清理工具UI，提供更好的用户体验和功能"""
    
    def __init__(self):
        super().__init__()
        self.title("智能清理工具")
        self.geometry("800x700")
        self.config(padx=20, pady=20)

        # 初始化配置对象（新增）
        self.config = CleanerConfig()  # 统一使用 self.config 作为配置对象
        
        # 设置图标
        try:
            self.iconbitmap("cleaner.ico")
        except:
            pass
        

        # 核心参数存储
        self.clean_types = []
        self.system_type = None
        self.custom_dirs = []
        self.check_folders = tk.BooleanVar(value=False)
        self.folder_depth = tk.IntVar(value=3)
        self.do_clean = False
        self.is_scanning = False
        self.recursive_var = tk.BooleanVar(value=self.config.recursive)
        self.full_scan = tk.BooleanVar(value=False)  # 新增：全盘扫描选项

        self.hash_algorithm_var = tk.StringVar(value='md5')
        self.hash_desc_var = tk.StringVar()
        
        # 创建UI组件
        self.create_widgets()
        
        # 加载上次使用的配置（如果有）
        self.load_settings()

    def update_log(self, message: str):
        """在日志区域显示扫描状态（关键）"""
        self.log_text.config(state=tk.NORMAL)
        self.log_text.insert(tk.END, f"[操作日志] {message}\n")
        self.log_text.config(state=tk.DISABLED)
        self.log_text.see(tk.END)

    
    def create_widgets(self):
        # 创建主框架
        main_frame = ttk.Frame(self)
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 左侧控制面板
        control_frame = ttk.LabelFrame(main_frame, text="清理设置")
        control_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 清理类型选择
        ttk.Label(control_frame, text="清理类型:").pack(anchor=tk.W, padx=5, pady=5)
        self.type_vars = {
            "duplicate": tk.IntVar(value=1),
            "package": tk.IntVar(value=1)
        }
        
        type_frame = ttk.Frame(control_frame)
        type_frame.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Checkbutton(type_frame, text="重复文件", variable=self.type_vars["duplicate"]).pack(anchor=tk.W)
        ttk.Checkbutton(type_frame, text="软件包", variable=self.type_vars["package"]).pack(anchor=tk.W)
        
        # 系统类型选择
        ttk.Label(control_frame, text="操作系统:").pack(anchor=tk.W, padx=5, pady=5)
        self.system_combobox = ttk.Combobox(control_frame, values=["自动检测", "Windows", "Linux", "macOS"])
        self.system_combobox.set("自动检测")
        self.system_combobox.pack(fill=tk.X, padx=5, pady=5)
        
        # 新增：全盘扫描选项
        ttk.Checkbutton(
            control_frame, 
            text="全盘扫描", 
            variable=self.full_scan,
            command=self.toggle_full_scan
        ).pack(anchor=tk.W, padx=5, pady=5)

         # 新增高级过滤设置（调整创建顺序避免变量未定义）
        filter_frame = ttk.LabelFrame(control_frame, text="目录过滤设置")
        filter_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 递归扫描复选框（使用独立 BooleanVar 绑定状态）
        self.recursive_var = tk.BooleanVar(value=True)
        # 修正：使用正确的配置对象属性
        # ttk.Checkbutton(
        #     filter_frame, 
        #     text="递归扫描子目录", 
        #     variable=self.config.recursive  # 修正为 config_obj
        # ).pack(anchor=tk.W, padx=5)
        
        
        # 自定义目录管理
        ttk.Label(control_frame, text="扫描目录:").pack(anchor=tk.W, padx=5, pady=5)
        
        dir_frame = ttk.Frame(control_frame)
        dir_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        self.dir_listbox = tk.Listbox(dir_frame, width=30, height=5)
        self.dir_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        dir_button_frame = ttk.Frame(dir_frame)
        dir_button_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=5)
        
        ttk.Button(dir_button_frame, text="添加", command=self.add_directory).pack(fill=tk.X, pady=2)
        ttk.Button(dir_button_frame, text="删除", command=self.remove_directory).pack(fill=tk.X, pady=2)
        ttk.Button(dir_button_frame, text="添加系统临时目录", command=self.add_system_temp_dirs).pack(fill=tk.X, pady=2)
        
        # 高级选项
        ttk.Label(control_frame, text="高级选项:").pack(anchor=tk.W, padx=5, pady=5)
        
        advanced_frame = ttk.Frame(control_frame)
        advanced_frame.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Checkbutton(advanced_frame, text="扫描重复文件夹", variable=self.check_folders).pack(side=tk.LEFT, padx=5)
        
        depth_frame = ttk.Frame(advanced_frame)
        depth_frame.pack(side=tk.LEFT, padx=5)
        
        ttk.Label(depth_frame, text="文件夹深度:").pack(side=tk.LEFT)
        self.depth_entry = ttk.Entry(depth_frame, textvariable=self.folder_depth, width=5)
        self.depth_entry.pack(side=tk.LEFT, padx=5)
        
        # 软件包扩展名配置
        ttk.Label(control_frame, text="软件包扩展名:").pack(anchor=tk.W, padx=5, pady=5)
        
        self.ext_entry = ttk.Entry(control_frame)
        self.ext_entry.insert(0, "exe,msi,zip,rar,7z,iso,dmg,pkg,deb,rpm")
        self.ext_entry.pack(fill=tk.X, padx=5, pady=5)
        
        # 右侧信息和操作区域
        info_frame = ttk.LabelFrame(main_frame, text="扫描信息")
        info_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 扫描进度条
        ttk.Label(info_frame, text="扫描进度:").pack(anchor=tk.W, padx=5, pady=5)
        self.progress_bar = ttk.Progressbar(info_frame, orient=tk.HORIZONTAL, length=300, mode='determinate')
        self.progress_bar.pack(fill=tk.X, padx=5, pady=5)
        
        # 操作按钮
        button_frame = ttk.Frame(info_frame)
        button_frame.pack(fill=tk.X, padx=5, pady=10)
        
        self.start_button = ttk.Button(button_frame, text="开始扫描", command=self.start_scan)
        self.start_button.pack(side=tk.LEFT, padx=5)
        
        self.stop_button = ttk.Button(button_frame, text="停止扫描", command=self.stop_scan, state=tk.DISABLED)
        self.stop_button.pack(side=tk.LEFT, padx=5)
        
        self.show_results_button = ttk.Button(button_frame, text="查看扫描结果", command=self.show_scan_results, state=tk.DISABLED)
        self.show_results_button.pack(side=tk.LEFT, padx=5)
        
        self.load_results_button = ttk.Button(button_frame, text="加载历史结果", command=self.load_scan_results)
        self.load_results_button.pack(side=tk.LEFT, padx=5)

         # 配置LabelFrame样式
        style = ttk.Style()
        style.configure('Active.TLabelframe.Label', foreground='black')
        style.configure('Disabled.TLabelframe.Label', foreground='gray60')
        style.map('Active.TLabelframe.Label', foreground=[('active', 'black')])
        style.map('Disabled.TLabelframe.Label', foreground=[('active', 'gray60')])
        
        
        self.clean_button = ttk.Button(button_frame, text="执行清理", command=self.start_clean, state=tk.DISABLED)
        self.clean_button.pack(side=tk.RIGHT, padx=5)
        
        # 日志区域
        ttk.Label(info_frame, text="操作日志:").pack(anchor=tk.W, padx=5, pady=5)
        self.log_text = scrolledtext.ScrolledText(info_frame, height=10, width=50)
        self.log_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        self.log_text.insert(tk.END, "欢迎使用智能清理工具，请配置清理选项并开始扫描...\n")
        self.log_text.config(state=tk.DISABLED)
        
        # 扫描结果摘要
        ttk.Label(info_frame, text="扫描结果摘要:").pack(anchor=tk.W, padx=5, pady=5)
        self.result_text = scrolledtext.ScrolledText(info_frame, height=10, width=50)
        self.result_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        self.result_text.config(state=tk.DISABLED)
        
        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        ttk.Label(self, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W).pack(side=tk.BOTTOM, fill=tk.X)

        # 新增高级过滤设置
        filter_frame = ttk.LabelFrame(control_frame, text="目录过滤设置")
        filter_frame.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Label(filter_frame, text="包含关键字（逗号分隔）:").pack(anchor=tk.W, padx=5, pady=2)
        self.keyword_entry = ttk.Entry(filter_frame)
        self.keyword_entry.pack(fill=tk.X, padx=5, pady=2)
        self.keyword_entry.insert(0, "install,setup,downloads")  # 示例关键字
        
        logic_frame = ttk.Frame(filter_frame)
        logic_frame.pack(fill=tk.X, padx=5, pady=2)
        
        ttk.Label(logic_frame, text="逻辑关系:").pack(side=tk.LEFT, padx=5)
        self.logic_combobox = ttk.Combobox(logic_frame, values=["或 (OR)", "与 (AND)"])
        self.logic_combobox.set("或 (OR)")
        self.logic_combobox.pack(side=tk.LEFT, padx=5)
        
        self.recursive_var = tk.BooleanVar(value=self.config.recursive)
        ttk.Checkbutton(filter_frame, text="递归扫描子目录", variable=self.recursive_var).pack(anchor=tk.W, padx=5)
        
        # 应用过滤设置按钮
        ttk.Button(filter_frame, text="应用过滤", command=self.apply_filter_settings).pack(side=tk.RIGHT, padx=5)

        # 在清理类型选择下方添加哈希算法设置
        type_frame = ttk.Frame(control_frame)
        type_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 哈希算法选择框架（初始状态为禁用）
        self.hash_frame = ttk.LabelFrame(control_frame, text="哈希算法设置")
        self.hash_frame.pack(fill=tk.X, padx=5, pady=5, anchor=tk.W)

        # 哈希算法选择组件
        algo_combobox = ttk.Combobox(
            self.hash_frame, 
            textvariable=self.hash_algorithm_var,
            values=list(CleanerConfig.get_hash_algorithms().keys()),
            state="readonly"
        )
        algo_combobox.pack(side=tk.LEFT, padx=5)
        algo_combobox.bind("<<ComboboxSelected>>", self.update_hash_description)

        # 算法描述标签
        ttk.Label(self.hash_frame, textvariable=self.hash_desc_var).pack(side=tk.LEFT, padx=5)

        # 初始更新描述
        self.update_hash_description()

        # 绑定重复文件复选框状态变化事件
        self.type_vars["duplicate"].trace_add("write", self.toggle_hash_settings)
        self.toggle_hash_settings()  # 初始状态设置

    def toggle_hash_settings(self, *args):
        """根据重复文件选择状态切换哈希算法设置可用性"""
        enable = self.type_vars["duplicate"].get() == 1
        
        # 设置子控件状态
        for child in self.hash_frame.winfo_children():
            if isinstance(child, ttk.Combobox):
                child.configure(state='readonly' if enable else 'disabled')
            elif isinstance(child, ttk.Widget):
                child.configure(state=tk.NORMAL if enable else tk.DISABLED)
        
        # 设置标签样式
        style_name = 'Active.TLabelframe' if enable else 'Disabled.TLabelframe'
        self.hash_frame.configure(style=style_name)

    def update_hash_description(self, event=None):
        """更新哈希算法描述"""
        algorithms = CleanerConfig.get_hash_algorithms()
        selected = self.hash_algorithm_var.get()
        desc = f"速度: {algorithms[selected]['speed']}, 安全性: {algorithms[selected]['security']}"
        self.hash_desc_var.set(desc)

    def apply_filter_settings(self):
        if not hasattr(self, 'manager'):
            messagebox.showwarning("警告", "请先开始扫描以初始化配置")
            return
        
        # 从 UI 组件获取过滤参数
        keywords = self.keyword_entry.get().split(',')
        logic = "or" if self.logic_combobox.get() == "或 (OR)" else "and"
        recursive = self.recursive_var.get()
        
        # 应用到管理器的配置对象
        self.manager.config.set_keyword_filters(keywords, logic, recursive)
        # 新逻辑：从 UI 变量同步状态到配置对象
        self.manager.config.recursive = self.config.recursive  # 修正为 config_obj

    def toggle_full_scan(self):
        """切换全盘扫描模式"""
        if self.full_scan.get():
            # 启用全盘扫描时，禁用目录列表
            self.dir_listbox.config(state=tk.DISABLED)
            for button in self.dir_listbox.master.winfo_children():
                if isinstance(button, ttk.Button):
                    button.config(state=tk.DISABLED)
        else:
            # 禁用全盘扫描时，启用目录列表
            self.dir_listbox.config(state=tk.NORMAL)
            for button in self.dir_listbox.master.winfo_children():
                if isinstance(button, ttk.Button):
                    button.config(state=tk.NORMAL)
    
    def add_directory(self):
        """添加自定义扫描目录"""
        if self.full_scan.get():
            messagebox.showinfo("提示", "全盘扫描模式下无法添加自定义目录")
            return
            
        dir_path = filedialog.askdirectory(title="选择扫描目录")
        if dir_path and dir_path not in self.custom_dirs:
            self.custom_dirs.append(dir_path)
            self.dir_listbox.insert(tk.END, dir_path)
            self.save_settings()
    
    def remove_directory(self):
        """删除选中的扫描目录"""
        if self.full_scan.get():
            messagebox.showinfo("提示", "全盘扫描模式下无法删除目录")
            return
            
        selected = self.dir_listbox.curselection()
        if selected:
            index = selected[0]
            del self.custom_dirs[index]
            self.dir_listbox.delete(index)
            self.save_settings()
    
    def add_system_temp_dirs(self):
        """添加系统临时目录到扫描列表"""
        if self.full_scan.get():
            messagebox.showinfo("提示", "全盘扫描模式下已包含系统临时目录")
            return
            
        # 先检测系统类型
        system_type = self.get_system_type() or CleanerConfig()._detect_system()
        
        # 获取系统临时目录
        config = CleanerConfig(system_type)
        temp_dirs = config.platform_config.get('temp_dirs', [])
        
        # 添加到列表中
        added_count = 0
        for dir_path in temp_dirs:
            if os.path.exists(dir_path) and dir_path not in self.custom_dirs:
                self.custom_dirs.append(dir_path)
                self.dir_listbox.insert(tk.END, dir_path)
                added_count += 1
        
        if added_count > 0:
            self.update_log(f"已添加 {added_count} 个系统临时目录")
            self.save_settings()
        else:
            self.update_log("没有可添加的系统临时目录")
    
    def get_system_type(self):
        """获取用户选择的系统类型"""
        user_input = self.system_combobox.get().lower()
        if user_input == "自动检测":
            return None
        return {"windows": "windows", "linux": "linux", "macos": "macos"}.get(user_input, "windows")
    
    def update_log(self, message: str):
        """在日志区域显示扫描状态（关键）"""
        self.log_text.config(state=tk.NORMAL)
        self.log_text.insert(tk.END, f"[扫描日志] {message}\n")
        self.log_text.config(state=tk.DISABLED)
        self.log_text.see(tk.END)
    
    def update_progress(self, value):
        """更新进度条"""
        self.progress_bar['value'] = value
        self.status_var.set(f"扫描进度: {value:.1f}%")
    
    def start_scan(self):
        """开始扫描过程"""
        if self.is_scanning:
            return
            
        self.clean_types = [k for k, v in self.type_vars.items() if v.get() == 1]
        if not self.clean_types:
            messagebox.showwarning("错误", "请至少选择一种清理类型")
            return

        self.system_type = self.get_system_type()
        
        # 确认全盘扫描
        if self.full_scan.get():
            if not messagebox.askyesno("确认全盘扫描", "全盘扫描会检查系统所有驱动器，可能需要较长时间并需要管理员权限。是否继续？"):
                return
        
        # 准备扫描目录
        scan_dirs = self.custom_dirs if not self.full_scan.get() else []
        
        try:
            self.manager = CleanManager(
                system_type=self.system_type,
                clean_dirs=scan_dirs,
                link_type='delete',
                full_scan=self.full_scan.get(),  # 传递全盘扫描标志
                hash_algorithm=self.hash_algorithm_var.get()  # 传递哈希算法
            )
            
             # 应用过滤设置（新增）
            self.apply_filter_settings()

            # 设置高级选项
            self.manager.config.check_folders = self.check_folders.get()
            self.manager.config.folder_depth = self.folder_depth.get()
            self.manager.config.recursive = self.recursive_var.get()  # 关键同步代码
            
            # 添加自定义扩展名
            custom_exts = self.ext_entry.get().split(',')
            custom_exts = [ext.strip().lower() for ext in custom_exts if ext.strip()]
            self.manager.config.add_custom_extensions(custom_exts)
            
            self.update_log("开始扫描...")
            if self.full_scan.get():
                self.update_log("🔍 正在进行全盘扫描，请耐心等待...")
            else:
                self.update_log(f"🔍 正在扫描指定目录: {', '.join(scan_dirs) if scan_dirs else '默认目录'}")
            
            self.progress_bar['value'] = 0
            self.show_results_button.config(state=tk.DISABLED)
            self.clean_button.config(state=tk.DISABLED)
            self.start_button.config(state=tk.DISABLED)
            self.stop_button.config(state=tk.NORMAL)
            self.is_scanning = True
            
            # 清空结果区域
            self.result_text.config(state=tk.NORMAL)
            self.result_text.delete(1.0, tk.END)
            self.result_text.config(state=tk.DISABLED)
            
            # 在单独的线程中运行扫描，避免阻塞UI
            import threading
            scan_thread = threading.Thread(target=self.run_scan_in_thread)
            scan_thread.daemon = True
            scan_thread.start()
            
        except Exception as e:
            messagebox.showerror("错误", f"扫描初始化失败: {str(e)}")
            self.update_log(f"❌ 扫描初始化失败: {str(e)}")
            self.is_scanning = False
            self.reset_ui_after_scan()
    
    def run_scan_in_thread(self):
        """在单独的线程中运行扫描"""
        try:
            self.manager.run_scan(self.clean_types, self.update_progress)
            # 扫描完成后弹出确认框
            import tkinter.messagebox
            tkinter.messagebox.showinfo("扫描结束", "扫描已完成！")  # 添加此行
            # 扫描完成后，将扫描结果显示在result_text中
            self.after(0, self.update_results_after_scan)
            
        except Exception as e:
            self.after(0, lambda: messagebox.showerror("错误", f"扫描失败: {str(e)}"))
            self.after(0, lambda: self.update_log(f"❌ 扫描失败: {str(e)}"))
        finally:
            self.after(0, self.reset_ui_after_scan)
    
    def reset_ui_after_scan(self):
        """扫描完成后重置UI状态"""
        self.start_button.config(state=tk.NORMAL)
        self.stop_button.config(state=tk.DISABLED)
        self.is_scanning = False
        self.status_var.set("就绪")
    
    def update_results_after_scan(self):
        """更新扫描结果显示"""
        scan_result = self.get_scan_result_text()
        self.result_text.config(state=tk.NORMAL)
        self.result_text.insert(tk.END, scan_result)
        self.result_text.config(state=tk.DISABLED)

        self.update_log("扫描完成，可点击“查看扫描结果”查看详情")
        self.show_results_button.config(state=tk.NORMAL)
        self.clean_button.config(state=tk.NORMAL)
        self.progress_bar['value'] = 100
    
    def stop_scan(self):
        """停止正在进行的扫描"""
        if self.is_scanning and hasattr(self, 'manager'):
            self.manager.stop_scan()
            self.update_log("正在停止扫描...")
    
    def get_scan_result_text(self):
        """获取扫描结果的文本摘要"""
        scan_result_text = ""
        for clean_type, path in getattr(self, 'manager', {}).scan_data.items():
            try:
                with open(path, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    if clean_type == "duplicate":
                        groups = data.get('groups', [])
                        duplicate_count = sum(len(group['files']) - 1 for group in groups)
                        total_size = sum(group.get('total_size', 0) for group in groups)
                        scan_result_text += f"重复文件扫描结果：发现 {duplicate_count} 个重复文件，可释放 {total_size/1024/1024:.2f} MB\n"
                        
                        folder_groups = data.get('folder_groups', [])
                        if folder_groups:
                            folder_count = sum(len(group['folders']) - 1 for group in folder_groups)
                            folder_size = sum(group.get('total_size', 0) for group in folder_groups)
                            scan_result_text += f"重复文件夹扫描结果：发现 {folder_count} 个重复文件夹，可释放 {folder_size/1024/1024:.2f} MB\n"
                    elif clean_type == "package":
                        package_files = data.get('clean_list', [])
                        package_count = len(package_files)
                        total_size = sum(os.path.getsize(f) for f in package_files if os.path.exists(f)) if package_files else 0
                        scan_result_text += f"软件包扫描结果：发现 {package_count} 个软件包，可释放 {total_size/1024/1024:.2f} MB\n"
            except Exception as e:
                scan_result_text += f"无法读取{clean_type}扫描结果: {str(e)}\n"
        return scan_result_text
    
    def show_scan_results(self):
        """显示扫描结果详情"""
        if not hasattr(self, 'manager') or not self.manager.scan_data:
            messagebox.showinfo("提示", "没有扫描结果可供显示")
            return
            
        for clean_type, path in self.manager.scan_data.items():
            try:
                with open(path, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    EnhancedScanResultsWindow(self, clean_type, data, self)  # 新增 self 参数传递
            except Exception as e:
                messagebox.showerror("错误", f"无法显示{clean_type}扫描结果: {str(e)}")
    
    def load_scan_results(self):
        import traceback
        file_path = filedialog.askopenfilename(
            title="选择扫描结果文件",
            filetypes=[("JSON files", "*.json")],
            initialdir="scan_results"
        )
        
        if not file_path:
            return
            
        try:
            file_path = str(Path(file_path).resolve())  # 双重规范化
            
            # 详细路径验证
            if not Path(file_path).exists():
                raise FileNotFoundError(f"路径不存在: {file_path}")
            if not Path(file_path).is_file():
                raise IsADirectoryError(f"路径是目录: {file_path}")
            if not os.access(file_path, os.R_OK):
                raise PermissionError(f"无读取权限: {file_path}")

            # 读取文件内容
            if not os.path.isfile(file_path):
                raise ValueError("文件路径无效或文件不存在")
                
            with open(file_path, 'r', encoding='utf-8') as f:
                 raw_content = f.read()
                 data = json.loads(raw_content)
                
                 # 深度规范化路径
                 def normalize_paths(obj):
                    if isinstance(obj, dict):
                        for key in list(obj.keys()):
                            if key == 'path':
                                obj[key] = str(Path(obj[key]).resolve())
                            else:
                                normalize_paths(obj[key])
                    elif isinstance(obj, list):
                        for item in obj:
                            normalize_paths(item)

                 # 修复时间戳数据
                 def fix_timestamps(obj):
                    if isinstance(obj, dict):
                        if 'mtime' in obj:
                            try:
                                obj['mtime'] = float(obj['mtime'])
                            except (TypeError, ValueError):
                                obj['mtime'] = datetime.now().timestamp()
                        for v in obj.values():
                            fix_timestamps(v)
                    elif isinstance(obj, list):
                        for item in obj:
                            fix_timestamps(item)
                 fix_timestamps(data)
                 normalize_paths(data)
            
            scan_type = data.get('scan_type', '未知类型')
            self.display_scan_results(scan_type, data, file_path)  # 不再传递处理方式
            
        except Exception as e:
                error_msg = (
                    f"加载失败详情：\n"
                    f"错误类型: {type(e).__name__}\n"
                    f"错误信息: {str(e)}\n"
                    f"文件路径: {file_path}\n"
                    f"文件大小: {Path(file_path).stat().st_size if Path(file_path).exists() else 0} 字节\n"
                    f"堆栈跟踪:\n{traceback.format_exc()}"
                )
                messagebox.showerror("严重错误", error_msg)
                self.update_log(f"❌ 加载失败详细信息:\n{error_msg}")

    def display_scan_results(self, scan_type: str, data: dict, file_path: str):
        """独立封装结果显示逻辑，避免重复代码"""
        self.result_text.config(state=tk.NORMAL)
        self.result_text.delete(1.0, tk.END)
        
        if scan_type == "duplicate":
            self.display_duplicate_results(data)
        elif scan_type == "package":
            self.display_package_results(data)
            
        self.result_text.config(state=tk.DISABLED)
        EnhancedScanResultsWindow(self, scan_type, data, self)  # 传递主应用引用
        self.update_log(f"已加载历史扫描结果：{os.path.basename(file_path)}")
        self.show_results_button.config(state=tk.NORMAL)
        self.clean_button.config(state=tk.NORMAL)
    

    def display_duplicate_results(self, data: dict):
        """显示重复文件/文件夹结果"""
        groups = data.get('groups', [])
        duplicate_count = sum(len(group['files']) - 1 for group in groups)
        total_size = sum(group.get('total_size', 0) for group in groups)
        self.result_text.insert(tk.END, f"重复文件扫描结果：发现 {duplicate_count} 个重复文件，可释放 {total_size/1024/1024:.2f} MB\n")
        
        folder_groups = data.get('folder_groups', [])
        if folder_groups:
            folder_count = sum(len(group['folders']) - 1 for group in folder_groups)
            folder_size = sum(group.get('total_size', 0) for group in folder_groups)
            self.result_text.insert(tk.END, f"重复文件夹扫描结果：发现 {folder_count} 个重复文件夹，可释放 {folder_size/1024/1024:.2f} MB\n")

    def display_package_results(self, data: dict):
        """显示软件包结果（严格过滤无效数据）"""
        package_files = [item for item in data.get('files', []) if isinstance(item, dict)]
        valid_files = [item for item in package_files if os.path.exists(item.get('path', ''))]
        package_count = len(valid_files)
        total_size = sum(item.get('size', 0) for item in valid_files)

        # 二次验证路径有效性
        for item in valid_files:
            if not Path(item['path']).exists():
                self.update_log(f"⚠️ 警告：文件不存在 {item['path']}")
                valid_files.remove(item)  # 移除非法路径

        
        self.result_text.insert(tk.END, f"软件包扫描结果：发现 {package_count} 个软件包，可释放 {total_size/1024/1024:.2f} MB\n")
    
    def start_clean(self):
        """开始清理过程"""
        if not hasattr(self, 'manager') or not self.manager.scan_data:
            messagebox.showinfo("提示", "没有扫描结果可供清理")
            return
            
        self.do_clean = True
        try:
            self.update_log("开始清理...")
            self.progress_bar['value'] = 0
            self.start_button.config(state=tk.DISABLED)
            self.show_results_button.config(state=tk.DISABLED)
            self.clean_button.config(state=tk.DISABLED)
            
            # 在单独的线程中运行清理，避免阻塞UI
            import threading
            clean_thread = threading.Thread(target=self.run_clean_in_thread)
            clean_thread.daemon = True
            clean_thread.start()
            
        except Exception as e:
            messagebox.showerror("错误", f"清理失败: {str(e)}")
            self.update_log(f"❌ 清理失败: {str(e)}")
            self.reset_ui_after_clean()
    
    def run_clean_in_thread(self):
        """在单独的线程中运行清理"""
        try:
            cleaned_count = self.manager.run_clean(self.clean_types)
            
            # 更新UI
            self.after(0, lambda: self.update_log(f"清理完成！共清理 {cleaned_count} 个项目"))
            self.after(0, lambda: messagebox.showinfo("清理完成", f"已成功清理 {cleaned_count} 个项目"))
            
            # 更新结果显示
            self.after(0, self.update_results_after_clean)
            
        except Exception as e:
            self.after(0, lambda: messagebox.showerror("错误", f"清理失败: {str(e)}"))
            self.after(0, lambda: self.update_log(f"❌ 清理失败: {str(e)}"))
        finally:
            self.after(0, self.reset_ui_after_clean)
    
    def reset_ui_after_clean(self):
        """清理完成后重置UI状态"""
        self.start_button.config(state=tk.NORMAL)
        self.progress_bar['value'] = 0
        self.status_var.set("就绪")
    
    def update_results_after_clean(self):
        """清理后更新结果显示"""
        scan_result = self.get_scan_result_text()
        self.result_text.config(state=tk.NORMAL)
        self.result_text.delete(1.0, tk.END)
        self.result_text.insert(tk.END, scan_result)
        self.result_text.config(state=tk.DISABLED)
        
        self.show_results_button.config(state=tk.NORMAL)
    
    def save_settings(self):
        """保存当前设置"""
        try:
            settings = {
                'system_type': self.system_combobox.get(),
                'custom_dirs': self.custom_dirs,
                'clean_types': [k for k, v in self.type_vars.items() if v.get() == 1],
                'check_folders': self.check_folders.get(),
                'folder_depth': self.folder_depth.get(),
                'package_exts': self.ext_entry.get(),
                'full_scan': self.full_scan.get(),  # 保存全盘扫描选项
                'include_keywords': self.keyword_entry.get().split(','),
                'keyword_logic': "or" if self.logic_combobox.get() == "或 (OR)" else "and",
                'recursive': self.config.recursive
            }
            
            with open('cleaner_settings.json', 'w', encoding='utf-8') as f:
                json.dump(settings, f, indent=2)
        except Exception as e:
            print(f"保存设置失败: {e}")
    
    def load_settings(self):
        """加载上次保存的设置"""
        try:
            if os.path.exists('cleaner_settings.json'):
                with open('cleaner_settings.json', 'r', encoding='utf-8') as f:
                    settings = json.load(f)
                    
                    if 'system_type' in settings:
                        self.system_combobox.set(settings['system_type'])
                    
                    if 'custom_dirs' in settings:
                        self.custom_dirs = settings['custom_dirs']
                        for dir_path in self.custom_dirs:
                            self.dir_listbox.insert(tk.END, dir_path)
                    
                    if 'clean_types' in settings:
                        for clean_type in settings['clean_types']:
                            if clean_type in self.type_vars:
                                self.type_vars[clean_type].set(1)
                    
                    if 'check_folders' in settings:
                        self.check_folders.set(settings['check_folders'])
                    
                    if 'folder_depth' in settings:
                        self.folder_depth.set(settings['folder_depth'])
                    
                    if 'package_exts' in settings:
                        self.ext_entry.delete(0, tk.END)
                        self.ext_entry.insert(0, settings['package_exts'])
                    
                    if 'full_scan' in settings:
                        self.full_scan.set(settings['full_scan'])
                        self.toggle_full_scan()  # 更新UI状态

                    if 'include_keywords' in settings:
                        self.keyword_entry.delete(0, tk.END)
                        self.keyword_entry.insert(0, ",".join(settings['include_keywords']))
                    if 'keyword_logic' in settings:
                        self.logic_combobox.set("或 (OR)" if settings['keyword_logic'] == "or" else "与 (AND)")
                    if 'recursive' in settings:
                        self.config.recursive = settings['recursive']
                        self.recursive_var.set(settings['recursive'])  # 同步到UI组件
        except Exception as e:
            print(f"加载设置失败: {e}")

if __name__ == "__main__":
    app = EnhancedCleanUI()
    app.mainloop()
